home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Moscow ML 1.31 / source code / mosml / src / compiler / Back.sml < prev    next >
Encoding:
Text File  |  1996-07-03  |  17.2 KB  |  525 lines  |  [TEXT/R*ch]

  1. (*  back.sml : translation of lambda terms to lists of instructions. *)
  2.  
  3. open List Fnlib Mixture Const Lambda Prim Instruct;
  4.  
  5. (* "isReturn" determines if we're in tail call position. *)
  6.  
  7. fun isReturn (Kreturn :: _ )             = true
  8.   | isReturn (Klabel _ :: Kreturn :: _ ) = true
  9.   | isReturn _                           = false
  10. ;
  11.  
  12. (* Label generation *)
  13.  
  14. val labelCounter = ref 0;
  15.  
  16. fun resetLabel() =
  17.   labelCounter := 0
  18. ;
  19.  
  20. fun new_label() =
  21.   (incr labelCounter; !labelCounter)
  22. ;
  23.  
  24. (* Add a label to a list of instructions. *)
  25.  
  26. fun labelCode C =
  27.   case C of
  28.     Kbranch lbl :: _ =>
  29.       (lbl, C)
  30.   | Klabel lbl :: _ =>
  31.       (lbl, C)
  32.   | _ =>
  33.       let val lbl = new_label()
  34.       in (lbl, Klabel lbl :: C) end
  35. ;
  36.  
  37. (* Generate a branch to the given list of instructions. *)
  38.  
  39. fun makeBranch C =
  40.   case C of
  41.     Kreturn :: _ =>
  42.       (Kreturn, C)
  43.   | Klabel _ :: Kreturn :: _ =>
  44.       (Kreturn, C)
  45.   | (branch as Kbranch _) :: _ =>
  46.       (branch, C)
  47.   | _ =>
  48.       let val lbl = new_label()
  49.       in (Kbranch lbl, Klabel lbl :: C) end
  50. ;
  51.  
  52. (* Discard all instructions up to the next label. *)
  53.  
  54. fun discardDeadCode C =
  55.   case C of
  56.     [] => []
  57.   | Klabel _ :: _ => C
  58.   | _ :: rest => discardDeadCode rest
  59. ;
  60.  
  61. (* Generate a jump through table, unless unnecessary. *)
  62.  
  63. exception JumpOut;
  64.  
  65. fun add_SwitchTable switchtable C =
  66.   let open Array infix 9 sub in
  67.     (for (fn i => if (switchtable sub i) <> (switchtable sub 0) then
  68.                     raise JumpOut
  69.                   else ())
  70.          1 (length switchtable - 1);
  71.      case C of
  72.          Klabel lbl :: C1 =>
  73.            if lbl = (switchtable sub 0) then C
  74.            else
  75.              Kbranch (switchtable sub 0) :: C
  76.        | _ =>
  77.           Kbranch (switchtable sub 0) :: C)
  78.     handle JumpOut =>
  79.       Kswitch switchtable :: C
  80.   end;
  81.  
  82. (* Compiling N-way integer branches *)
  83.  
  84. (* Input: a list of (key, action) pairs, where keys are integers. *)
  85. (* Output: a decision tree with the format below *)
  86.  
  87. datatype DecisionTree =
  88.     DTfail
  89.   | DTinterval of DecisionTree * Decision * DecisionTree
  90.  
  91. withtype Decision =
  92. {
  93.   low: int,
  94.   act: Lambda Array.array,
  95.   high: int
  96. };
  97.  
  98. fun arrayOfList xs =
  99.   if null xs then
  100.     Array.array0
  101.   else
  102.     let open Array
  103.         val len = List.length xs
  104.         val a = array(len, hd xs)
  105.         fun init [] k = ()
  106.           | init (x::xs) k =
  107.               (update(a, k, x); init xs (k+1))
  108.     in init xs 0; a end
  109. ;
  110.  
  111. fun compileNBranch int_of_key clauses =
  112.   let open Array infix 9 sub
  113.       val clauses_i =
  114.         map (fn (key, act) => (int_of_key key : int, act)) clauses
  115.       val clauses_s =
  116.         Sort.sort (fn (key1, act1) => fn (key2, act2) => key1 <= key2)
  117.                   clauses_i
  118.       val keyv = arrayOfList (map fst clauses_s)
  119.       val actv = arrayOfList (map snd clauses_s)
  120.       val n    = length keyv
  121.       fun extractAct start stop =
  122.         let val a =
  123.               array((keyv sub stop) - (keyv sub start) + 1, Lstaticfail)
  124.         in
  125.           for (fn i =>
  126.                  update(a, (keyv sub i) - (keyv sub start), actv sub i))
  127.               start stop;
  128.           a
  129.         end
  130.       (* Now we partition the set of keys keyv into maximal
  131.          dense enough segments. A segment is dense enough
  132.          if its span (max point - min point) is less
  133.          than four times its size (number of points). *)
  134.       fun partition start =
  135.         if start >= n then [] else
  136.         let val stop = ref (n-1) in
  137.           while (keyv sub !stop) - (keyv sub start) >= 255 orelse
  138.                 (keyv sub !stop) - (keyv sub start) > 4 * (!stop - start)
  139.           do decr stop;
  140.           (* We've found a segment that is dense enough.
  141.              In the worst case, !stop = start and the segment is
  142.              a single point *)
  143.           (* Now build the vector of actions *)
  144.           { low = keyv sub start,
  145.             act = extractAct start (!stop),
  146.             high = keyv sub !stop } :: partition (!stop + 1)
  147.         end
  148.       val part = arrayOfList (partition 0)
  149.       (* We build a balanced binary tree *)
  150.       fun make_tree start stop =
  151.         if start > stop then
  152.           DTfail
  153.         else
  154.           let val middle = (start + stop) div 2 in
  155.             DTinterval(make_tree start (middle-1),
  156.                        part sub middle,
  157.                        make_tree (middle+1) stop)
  158.           end
  159.   in make_tree 0 (length part - 1) end
  160. ;
  161.  
  162. (* To check if a switch construct contains tags that are unknown at
  163.    compile-time (i.e. exception tags). *)
  164.  
  165. fun switch_contains_exception_tags clauses =
  166.   exists (fn (EXNtag _, _) => true | _ => false) clauses
  167. ;
  168.  
  169. (* Inversion of a boolean test ( < becomes >= and so on) *)
  170.  
  171. val invertPrimTest = fn
  172.     PTeq => PTnoteq
  173.   | PTnoteq => PTeq
  174.   | PTnoteqimm x => fatalError "invertPrimTest"
  175.   | PTlt => PTge
  176.   | PTle => PTgt
  177.   | PTgt => PTle
  178.   | PTge => PTlt
  179. ;
  180.  
  181. val invertBoolTest = fn
  182.     Peq_test => Pnoteq_test
  183.   | Pnoteq_test => Peq_test
  184.   | Pint_test t => Pint_test(invertPrimTest t)
  185.   | Pfloat_test t => Pfloat_test(invertPrimTest t)
  186.   | Pstring_test t => Pstring_test(invertPrimTest t)
  187.   | Pnoteqtag_test t => fatalError "invertBoolTest"
  188. ;
  189.  
  190. (* Production of an immediate test *)
  191.  
  192. val testForAtom = fn
  193.     INTscon x => Pint_test(PTnoteqimm x)
  194.   | CHARscon x => Pint_test(PTnoteqimm (Char.ord x))
  195.   | REALscon x => Pfloat_test(PTnoteqimm x)
  196.   | STRINGscon x => Pstring_test(PTnoteqimm x)
  197. ;
  198.  
  199. (* To keep track of function bodies that remain to be compiled. *)
  200.  
  201. val stillToCompile  = (Stack.new () : (Lambda * int) Stack.t);
  202.  
  203. (* The translator from lambda terms to lists of instructions.
  204.  
  205.    staticfail : the label where Lstaticfail must branch.
  206.    lambda : the lambda term to compile.
  207.    C : the continuation, i.e. the code that follows the code for lambda.
  208.  
  209.    The tests on the continuation detect tail-calls and avoid jumps to jumps,
  210.    or jumps to function returns.
  211.  
  212. *)
  213.  
  214. fun compileExp staticfail =
  215.   let
  216.     open Array infix 9 sub
  217.  
  218.     fun compexp exp C =
  219.       case exp of
  220.         Lvar n =>
  221.             Kaccess n :: C
  222.       | Lconst cst =>
  223.           (case C of
  224.                Kquote _      :: _ => C
  225.              | Kget_global _ :: _ => C
  226.              | Kaccess _     :: _ => C
  227.              | Kpushmark     :: _ => C
  228.              | _ => Kquote cst :: C)
  229.       | Lapply(body, args) =>
  230.           (case C of
  231.              Kreturn :: C' =>
  232.                compExpList args (Kpush :: compexp body (Ktermapply :: C'))
  233.            | Klabel _ :: Kreturn :: _ =>
  234.                compExpList args (Kpush :: compexp body (Ktermapply :: C))
  235.            | _ =>
  236.                Kpushmark ::
  237.                compExpList args (Kpush :: compexp body (Kapply :: C)))
  238.       | Lfn body =>
  239.           if isReturn C then
  240.             Kgrab :: compexp body C
  241.           else
  242.             let val lbl = new_label() in
  243.               Stack.push (body, lbl) stillToCompile;
  244.               Kclosure lbl :: C
  245.             end
  246.       | Llet(args, body) =>
  247.           let val C1 = if isReturn C then C
  248.                        else Kendlet(List.length args) :: C
  249.               fun comp_args [] =
  250.                     compexp body C1
  251.                 | comp_args (exp::rest) =
  252.                     compexp exp (Klet :: comp_args rest)
  253.           in comp_args args end
  254.       | Lletrec([Lfn f], body) =>
  255.           let val C1 = if isReturn C then C else Kendlet 1 :: C
  256.               val lbl = new_label()
  257.           in
  258.             Stack.push (f, lbl) stillToCompile;
  259.             Kletrec1 lbl :: compexp body C1
  260.           end
  261.       | Lletrec(args, body) =>
  262.           let val size = List.length args
  263.               val C1 = if isReturn C then C else Kendlet size :: C
  264.               fun comp_args i = fn
  265.                   [] =>
  266.                     compexp body C1
  267.                 | exp :: rest =>
  268.                     compexp exp (Kpush :: Kaccess i :: Kprim Pupdate ::
  269.                                 comp_args (i-1) rest)
  270.           in
  271.             foldR
  272.               (fn e => fn C => Kprim(Pdummy 2) :: Klet :: C)
  273.               (comp_args (size-1) args) args
  274.           end
  275.       | Lprim(Pget_global uid, []) =>
  276.             Kget_global uid :: C
  277.       | Lprim(Pset_global uid, [exp]) =>
  278.             compexp exp (Kset_global uid :: C)
  279.       | Lprim(Pmakeblock tag, explist) =>
  280.             compExpList explist (Kmakeblock(tag, List.length explist) :: C)
  281.       | Lprim(Pnot, [exp]) =>
  282.           (case C of
  283.                Kbranchif lbl :: C' =>
  284.                  compexp exp (Kbranchifnot lbl :: C')
  285.              | Kbranchifnot lbl :: C' =>
  286.                  compexp exp (Kbranchif lbl :: C')
  287.              | _ =>
  288.                  compexp exp (Kprim Pnot :: C))
  289.       | Lprim(p as Ptest tst, explist) =>
  290.           (case C of
  291.                Kbranchif lbl :: C' =>
  292.                  compExpList explist (Ktest(tst,lbl) :: C')
  293.              | Kbranchifnot lbl :: C' =>
  294.                  compExpList explist (Ktest(invertBoolTest tst,lbl) :: C')
  295.              | _ =>
  296.                  compExpList explist (Kprim p :: C))
  297.       | Lprim(Praise, explist) =>
  298.             compExpList explist (Kprim Praise :: discardDeadCode C)
  299.       | Lprim(p, explist) =>
  300.             compExpList explist (Kprim p :: C)
  301.       | Lstatichandle(body, Lstaticfail) =>
  302.             compexp body C
  303.       | Lstatichandle(body, handler) =>
  304.           let val (branch1, C1) = makeBranch C
  305.               and lbl2 = new_label()
  306.           in
  307.             compileExp lbl2 body
  308.                        (branch1 :: Klabel lbl2 :: compexp handler C1)
  309.           end
  310.       | Lstaticfail =>
  311.           Kbranch staticfail :: discardDeadCode C
  312.       | Lhandle(body, handler) =>
  313.           let val (branch1, C1) = makeBranch C
  314.               val lbl2 = new_label()
  315.               val C2 = if isReturn C1 then C1 else Kendlet 1 :: C1 
  316.           in
  317.             Kpushtrap lbl2 ::
  318.               compexp body
  319.                       (Kpoptrap :: branch1 :: Klabel lbl2 
  320.                          :: compexp handler C2)
  321.           end
  322.       | Lif(cond, ifso, ifnot) =>
  323.             compTest2 cond ifso ifnot C
  324.       | Lseq(exp1, exp2) =>
  325.             compexp exp1 (compexp exp2 C)
  326.       | Lwhile(cond, body) =>
  327.           let val lbl1 = new_label() 
  328.               and lbl2 = new_label() 
  329.           in
  330.             Kbranch lbl1 :: Klabel lbl2 :: Kcheck_signals ::
  331.             compexp body (Klabel lbl1 :: compexp cond (
  332.               Kbranchif lbl2 :: Kquote constUnit :: C))
  333.           end
  334.       | Landalso(exp1, exp2) =>
  335.           (case C of
  336.                Kbranch lbl :: _  =>
  337.                  compexp exp1 (Kstrictbranchifnot lbl :: compexp exp2 C)
  338.              | Kbranchifnot lbl :: _ =>
  339.                  compexp exp1 (Kbranchifnot lbl :: compexp exp2 C)
  340.              | Kbranchif lbl :: C' =>
  341.                  let val (lbl1, C1) = labelCode C' in
  342.                    compexp exp1 (Kbranchifnot lbl1 ::
  343.                                  compexp exp2 (Kbranchif lbl :: C1))
  344.                  end
  345.              | Klabel lbl :: _ =>
  346.                  compexp exp1 (Kstrictbranchifnot lbl :: compexp exp2 C)
  347.              | _ =>
  348.                  let val lbl = new_label() in
  349.                    compexp exp1 (Kstrictbranchifnot lbl ::
  350.                                  compexp exp2 (Klabel lbl :: C))
  351.                  end)
  352.       | Lorelse(exp1, exp2) =>
  353.           (case C of
  354.                Kbranch lbl :: _  =>
  355.                  compexp exp1 (Kstrictbranchif lbl :: compexp exp2 C)
  356.              | Kbranchif lbl :: _  =>
  357.                  compexp exp1 (Kbranchif lbl :: compexp exp2 C)
  358.              | Kbranchifnot lbl :: C' =>
  359.                  let val (lbl1, C1) = labelCode C' in
  360.                    compexp exp1 (Kbranchif lbl1 ::
  361.                                  compexp exp2 (Kbranchifnot lbl :: C1))
  362.                  end
  363.              | Klabel lbl :: _ =>
  364.                  compexp exp1 (Kstrictbranchif lbl :: compexp exp2 C)
  365.              | _ =>
  366.                  let val lbl = new_label() in
  367.                    compexp exp1 (Kstrictbranchif lbl ::
  368.                                  compexp exp2 (Klabel lbl :: C))
  369.                  end)
  370.  
  371.       | Lcase(arg, clauses) =>
  372.           let val C1 =
  373.             if case clauses of
  374.                    (INTscon _, _) :: _ => true
  375.                  | (CHARscon _, _) :: _ => true
  376.                  | _ => false
  377.             then
  378.               compDecision (compileNBranch intOfAtom clauses) C
  379.             else
  380.               compTests
  381.                 (map (fn (cst, act) => (testForAtom cst, act)) clauses) C
  382.           in compexp arg C1 end
  383.  
  384.       | Lswitch(1, arg, [(CONtag(_,_), exp)]) =>
  385.           compexp exp C
  386.           (* We assume the argument to be safe (not producing side-effects
  387.              and always terminating),
  388.              because switches are generated only by the match compiler *)
  389.       | Lswitch(2, arg, [(CONtag(0,_), exp0)]) =>
  390.           compexp arg (Kbranchif staticfail :: compexp exp0 C)
  391.       | Lswitch(2, arg, [(CONtag(1,_), exp1)]) =>
  392.           compexp arg (Kbranchifnot staticfail :: compexp exp1 C)
  393.       | Lswitch(2, arg, [(CONtag(0,_), exp0), (CONtag(1,_), exp1)]) =>
  394.           compTest2 arg exp1 exp0 C
  395.       | Lswitch(2, arg, [(CONtag(1,_), exp1), (CONtag(0,_), exp0)]) =>
  396.           compTest2 arg exp1 exp0 C
  397.       | Lswitch(size, arg, clauses) =>
  398.           let val C1 =
  399.             if switch_contains_exception_tags clauses then
  400.               compTests
  401.                 (map (fn (tag,act) => (Pnoteqtag_test tag, act)) clauses) C
  402.             else if List.length clauses >= size - 5 then
  403.               Kprim Ptag_of :: compDirectSwitch size clauses C
  404.             else
  405.               Kprim Ptag_of ::
  406.                 compDecision (compileNBranch intOfAbsoluteTag clauses) C
  407.           in compexp arg C1 end
  408.       | Lunspec =>
  409.           C
  410.       | Lshared(exp, lbl_ref) =>
  411.           if !lbl_ref = Nolabel then
  412.             let val lbl = new_label() in
  413.               lbl_ref := lbl;
  414.               Klabel lbl :: compexp exp C
  415.             end
  416.           else
  417.             Kbranch (!lbl_ref) :: discardDeadCode C
  418.  
  419.     and compExpList [] C = C
  420.       | compExpList [exp] C = compexp exp C
  421.       | compExpList (exp::rest) C =
  422.           compExpList rest (Kpush :: compexp exp C)
  423.  
  424.     and compTest2 cond ifso ifnot C =
  425.       let val (branch1, C1) = makeBranch C
  426.           and lbl2 = new_label() 
  427.       in
  428.         compexp cond (Kbranchifnot lbl2 ::
  429.                      compexp ifso 
  430.                      (branch1 :: Klabel lbl2 :: compexp ifnot C1))
  431.       end
  432.  
  433.     and compTests clauses C =
  434.       let val (branch1, C1) = makeBranch C
  435.           fun comp [] =
  436.                 fatalError "compTests"
  437.             | comp [(test,exp)] =
  438.                 Ktest(test, staticfail) :: compexp exp C1
  439.             | comp ((test,exp)::rest) =
  440.                 let val lbl = new_label() in
  441.                   Ktest(test, lbl) :: 
  442.                     compexp exp (branch1 :: Klabel lbl :: comp rest)
  443.                 end
  444.       in comp clauses end
  445.  
  446.     and compSwitch v branch1 C =
  447.         let val switchtable =
  448.               array(length v, staticfail)
  449.             fun comp_cases n =
  450.               if n >= length v then
  451.                 C
  452.               else
  453.                 let val (lbl, C1) =
  454.                       labelCode (compexp (v sub n) 
  455.                                          (branch1 :: comp_cases (n+1)))
  456.                 in 
  457.                   update(switchtable, n, lbl); C1 
  458.                 end
  459.         in add_SwitchTable switchtable (discardDeadCode(comp_cases 0)) end
  460.  
  461.     and compDecision tree C =
  462.       let val (branch1, C1) = makeBranch C
  463.           fun comp_dec DTfail C =
  464.                 Kbranch staticfail :: discardDeadCode C
  465.             | comp_dec (DTinterval(left, dec, right)) C =
  466.                 let val (lbl_right, Cright) =
  467.                       case right of
  468.                           DTfail => (staticfail, C)
  469.                         | _      => labelCode (comp_dec right C)
  470.                     val (lbl_left, Cleft) =
  471.                       case left of
  472.                           DTfail => (staticfail, Cright)
  473.                         | _ =>      labelCode (comp_dec left Cright)
  474.                     val {low, act, high} = dec
  475.                 in
  476.                   Kbranchinterval(low, high, lbl_left, lbl_right) ::
  477.                   (case length act of
  478.                        1 => compexp (act sub 0) (branch1 :: Cleft)
  479.                      | _ => compSwitch act branch1 Cleft)
  480.                 end
  481.       in comp_dec tree C1 end
  482.  
  483.     and compDirectSwitch size clauses C =
  484.       let val (branch1, C1) = makeBranch C
  485.           val switchtable = array(size, staticfail)
  486.           fun comp_case [] =
  487.                 fatalError "compSwitch"
  488.             | comp_case [(tag, exp)] =
  489.                 let val (lbl, C2) = labelCode (compexp exp C1) in
  490.                   update(switchtable, intOfAbsoluteTag tag, lbl);
  491.                   C2
  492.                 end
  493.             | comp_case ((tag, exp) :: rest) =
  494.                 let val (lbl, C2) =
  495.                   labelCode (compexp exp (branch1 :: comp_case rest)) 
  496.                 in
  497.                   update(switchtable, intOfAbsoluteTag tag, lbl);
  498.                   C2
  499.                 end
  500.       in add_SwitchTable switchtable (discardDeadCode(comp_case clauses)) end
  501.  
  502.   in compexp end
  503. ;
  504.  
  505. fun compileRest C =
  506.   let val (exp, lbl) = Stack.pop stillToCompile in
  507.     compileRest (Klabel lbl :: compileExp Nolabel exp (Kreturn :: C))
  508.   end
  509.   handle Stack.Empty =>
  510.     C
  511. ;
  512.  
  513. fun compileLambda is_pure exp =
  514.   let val () = Stack.clear stillToCompile
  515.       val () = resetLabel()
  516.       val init_code =
  517.             compileExp Nolabel exp []
  518.       val function_code =
  519.             compileRest [] 
  520.   in
  521.     { kph_is_pure = is_pure,
  522.       kph_inits   = init_code,
  523.       kph_funcs   = function_code }
  524.   end;
  525.